package relu.R.landscape;

import jfm.utils.*;

import relu.weeds.*;
import relu.R.birds.*;
import jfm.model.*;
import jfm.lp.ModelComponent.MCType;
import jfm.model.Types.*;

import java.util.*;
import jfm.model.RotationMatrix;
import relu.R.weeds.WeedSpecies;
import jfm.mou.*;

/** A Farm in the landscape */
public class LSFarm {
	private final Farm farmLP; // Must stay private!!!!
	public final FarmArea location;
	public final double area;
	public final Landscape parentLandscape;
	private RotationMatrix rotationMatrix=null;
	static CropType[] cropTypes=new CropType[]{CropType.DRIEDPEAS,CropType.LINSEED,CropType.SPRINGBARLEY,CropType.WINTERBARLEY,
			CropType.WINTERBEANS,CropType.SPRINGBEANS,CropType.WINTERWHEAT,CropType.SETASIDE,CropType.WOSR,CropType.WAREPOTATOES};

	public final ArrayList<Field> fields=new ArrayList<Field>();

	public void setDistanceFromSugarbeetFactory(double distance,double costPerKmPerHa){
		
		if ( farmLP.cropping.baseCropTypes().contains(CropType.SUGARBEET)){
			Crop cp = farmLP.cropping.getCrop(CropType.SUGARBEET);
			double[] amounts={distance};
			
			CropInput inp=new CropInput(amounts,costPerKmPerHa,VariableType.TRANSPORT);
			cp.resetInput(VariableType.TRANSPORT, inp);		
		}
	}
	
	private LSFarm(Farm lp_,Landscape parent,FarmArea loc,double a){
		
		farmLP=lp_;
		area=a;
		if ( loc!=null){
			farmLP.landUse.setArea(loc.LParea);
		}
		location=loc;
		parentLandscape=parent;
	}
	
	public double stubbleProbability(CropType from,CropType to){
		double sa=StubblesComponent.stubbleAreaForRotation(from, to, 3, farmLP);
//		double total=farmLP.cropping.getCrop(to).getSolvedArea();
		
		double total=farmLP.rotations.getSolvedRotationMatrix().getArea(from, to);
		if ( total <= 0.0001 || sa > (total+0.0001) ){
//			String str= Output.solution(farmLP, false);
//			throw new Error("Invalid rotation area "+total+" "+sa+" "+from+" "+to+"\n"+str);
			return 0;
		}
	//	System.out.println(from+ " to "+to+" "+"sa "+sa+" total "+total);
		return sa/total;
	}
	
	/*
	public void setArea(double newA){
		farmLP.landUse.setArea(newA);		
		area=newA;
		location.setRadius(Math.sqrt(area/3.14));
	}
	public void setPosition(double x,double y){
		location.setPos(x,y);
	}*/
	public void setWinterStubbleSubsidy(double value){
		jfm.model.StubblesComponent mc= (jfm.model.StubblesComponent)farmLP.getModelComponent(MCType.STUBBLES);
		mc.setSubsidy(value);
	}
	
	public void setLocationInfo(double soiltype,double rainfall){
		farmLP.setLocation(new jfm.model.Location(soiltype,rainfall,26));
		farmLP.setFormulaVariables(); // Need to call this to ensure a reset of all formulae
	}
	
	public void setPriceForCrop(CropType ct,double price){
		if (farmLP.cropping.getCrops().containsKey(ct)){
			farmLP.cropping.getCrop(ct).resetPrimaryPrice(price);
		}
	}
	public double distanceFrom(double boxl,double x,double y){
		double dx=location.xPos-x;
		double dy=location.yPos-y;
		return location.minImageDistance(boxl, Math.sqrt(dx*dx+dy*dy));
	}
	
	public double getHedgeDensity(){
//		return (farmLP.landUse.getHedgeLength()/Math.sqrt(farmLP.landUse.getArea()));
		
		return (farmLP.getValueForObjective(ObjectiveType.HEDGEROWS)/farmLP.landUse.getArea());
	}

	public void applyMOUToFarm(FarmerMOU mou){
		FarmerMOU.applyToFarm(mou, farmLP);
	}
	public void addField(Field fld){fields.add(fld);};
	
	public static LSFarm fromXML(String file,Landscape parent,FarmArea loc,double a){
	//	System.out.println("New LSFarm "+file+" "+loc+" "+a);
		return new LSFarm(Farm.fromXML(file),parent,loc,a);
	}
	
	public void assignFieldsToBirds(List<Bird> birds){
		for(Field fl:fields){
			fl.assignToNewBirdsInRange(birds);
		}
	}
	
	public void solve() throws BadModelException , GLPKException {
		if (farmLP.solve(true) != jfm.lp.LPX.LPX_OPT){
			throw new Error("Failed to solve LP for farm ");
		}
		rotationMatrix=farmLP.rotations.getSolvedRotationMatrix();
		System.out.println(Output.solution(farmLP, false));
	}
	public RotationMatrix getRotationMatrix() throws NullPointException {
		if ( rotationMatrix != null){
			return rotationMatrix;
		} else {
			throw new NullPointException("Can't get rotation matrix because farm not yet solved");
		}
	}
	
	public double getProfit(){
		return farmLP.getValueForObjective(ObjectiveType.PROFIT);
	}
	
	public double getProportionOfFieldsInStubble(){
		double stub=0;
		for(Field fld:fields){
		//	System.out.println(fld.cropTransition());
			if ( fld.isStubble()){
				stub+=1;
			}
		}
		return stub/(double)fields.size();
	}
	
	public double getWinterStubbleArea(){
		if (farmLP.objectives().containsKey(ObjectiveType.WINTERSTUBBLE)){
			return farmLP.getValueForObjective(ObjectiveType.WINTERSTUBBLE);
		} else {
			throw new Error("No Winter Stubble Objective Defined");
		}
	}
	
	
	public String toString(){
		//		System.out.println(Output.oneLineSummary(farm.farmLP));
		return Output.solution(farmLP, false);
	}
	public static String oneLineCropHeader(){
		StringBuffer buff=new StringBuffer();

		for(BirdSpecies bsp:Landscape.birdSpecies){		
			buff.append(bsp+",");
		}
		for(WeedSpecies sp:WeedSpecies.values()){
			buff.append(sp+",");
		}
		for(CropType type:cropTypes){
			buff.append(type+",");
		}
		buff.append("HEDGES,");
		buff.append("PROFIT");
		return buff.toString();
	}

	public double getTotalBirdFood(BirdSpecies bsp){
		double total=0;
		for(WeedSpecies sp:WeedSpecies.values()){
			total+=bsp.foodFromWeedSpecies(getWeedPopulation(sp),sp);
		}
		return total;
	}
	
	public double getWeedPopulation(WeedSpecies sp){
		double total=0;
		for(Field fld:fields){
			total+=fld.getDensity(sp);
		}
		return total/(fields.size());
	}
	
	public double cropArea(CropType tp){
		if ( farmLP.cropping.getCrops().get(tp)!=null){
			return farmLP.cropping.getCrops().get(tp).getSolvedArea();
		} else {
			return 0.0;
		}
	}
	
	public String oneLineCropOutput(){
		StringBuffer buff=new StringBuffer();
		for(BirdSpecies bsp:Landscape.birdSpecies){			
			buff.append(getTotalBirdFood(bsp)+",");
		}
		for(WeedSpecies sp:WeedSpecies.values()){
			buff.append(getWeedPopulation(sp)+",");
		}
				CroppingComponent cropping=farmLP.cropping;
		Map<CropType,Crop> crops=cropping.getCrops();
		for(CropType type:cropTypes){	
			if(crops.containsKey(type)){	
				buff.append(crops.get(type).getSolvedArea());
			}
			buff.append(",");
		}
		buff.append(farmLP.getValueForObjective(ObjectiveType.HEDGEROWS)+",");
		buff.append(farmLP.getValueForObjective(Types.ObjectiveType.PROFIT));
		
		return buff.toString();
	}
	
}
